home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
lisp
/
wcl-21.lha
/
wcl-2.1
/
src
/
compiler
/
cross
/
macros.lisp
< prev
Wrap
Lisp/Scheme
|
1992-09-10
|
11KB
|
374 lines
;;; (C) Copyright 1990-1992 by Wade L. Hennessey. All rights reserved.
(in-package "W")
(defmacro letf ((accessor-form new-value) &body body)
(let ((old-value (gensym "OLD-VALUE-")))
`(let ((,old-value ,accessor-form))
(unwind-protect (progn (setf ,accessor-form ,new-value)
,@body)
(setf ,accessor-form ,old-value)))))
(defmacro iterate (name var-vals &body body)
`(labels ((,name ,(mapcar #'first var-vals)
,@body))
(,name ,@(mapcar #'second var-vals))))
(defmacro key-list-iterate (name (ivar list-form &optional done-form)
var-init-pairs
&body body)
(let ((iteration-label
(gensym (concatenate 'string (symbol-name name) "-")))
(remaining-list (gensym "REMAINING-LIST-"))
(vars (mapcar #'first var-init-pairs))
(vals (mapcar #'second var-init-pairs)))
`(macrolet ((,name (&key ,@(mapcar #'(lambda (var)
`(,var ',var))
vars))
(list ',iteration-label
(list 'cdr ',remaining-list)
,@vars)))
(labels ((,iteration-label ,(cons remaining-list vars)
(if (null ,remaining-list)
,done-form
(let ((,ivar (car ,remaining-list)))
,@body))))
(,iteration-label ,list-form ,@vals)))))
;;; Each name should be an object which may be coerced into
;;; a string. Return a symbol whose print-name is the concatenation
;;; of those strings.
(defun names->symbol (&rest names)
(intern (apply #'concatenate
'string
(mapcar #'string names))))
(defun tree-find (e tree)
(labels ((loopy (rest)
(if (atom rest)
(if (null rest)
nil
(eq e rest))
(or (loopy (car rest)) (loopy (cdr rest))))))
(loopy tree)))
(defun upto (e l)
(nreverse (cdr (member e (reverse l)))))
;;; Return real body and decls
(defun parse-body (body)
(iterate separate ((rest (if (stringp (car body)) ; discard doc string
(if (null (cdr body))
body
(cdr body))
body))
(decls nil))
(let ((form (car rest)))
(if (or (atom form)
(not (eq (car form) 'declare)))
(values rest ; real body
decls)
(separate (cdr rest) (append (cdr form) decls))))))
;;; Call INIT-FUNC N times, returing the results in a list.
(defun n-list (n init-func)
(if (= n 0)
nil
(cons (funcall init-func) (n-list (1- n) init-func))))
;;; Return every Nth element of L (for N >= 1). The odd
;;; part is that we always start with the first element.
(defun every-n (n l)
(iterate doit ((i 1)
(rest l))
(cond ((null rest) nil)
((= i 1) (cons (car rest) (doit n (cdr rest))))
(t (doit (1- i) (cdr rest))))))
(defun every-even (l)
(every-n 2 l))
(defun every-odd (l)
(every-n 2 (cdr l)))
(defun walk (func l)
(if (atom l)
(if (null l)
nil
(funcall func l))
(or (walk func (car l)) (walk func (cdr l)))))
(defun combos (objs)
(iterate loopy ((rest objs)
(combo nil))
(if (null rest)
(list (reverse combo))
(loop for x in (car rest)
nconcing (loopy (cdr rest) (cons x combo))))))
;;; INCREDIBLE! Common Lisp doesn't provide a standard function
;;; for printing the time of day out to a stream!
;;; I'm suprised there isn't a format directive to do this...
(defun print-time (&key (stream t) (universal-time (get-universal-time))
24-hour-time)
(multiple-value-bind (seconds
minutes
hours
day
month
year
day-of-week
daylight-savings
time-zone)
(decode-universal-time universal-time)
(declare (ignore daylight-savings time-zone))
(let ((am? (< hours 12)))
(format stream "~A:~2,'0D:~2,'0D ~Aon ~A, ~A ~A, ~A"
(if 24-hour-time
hours
(let ((h (if am? hours (- hours 12))))
(if (= h 0) 12 h)))
minutes
seconds
(if 24-hour-time
""
(if am? "am " "pm "))
(svref #("Monday" "Tuesday" "Wednesday" "Thursday"
"Friday" "Saturday" "Sunday")
day-of-week)
(svref #("January" "February" "March" "April" "May"
"June" "July" "August" "September" "October"
"November" "December")
(1- month))
day
year))))
;;; This should probably be inline.
(defun collect (func args)
(do ((rest (cdr args) (cdr rest))
(result (car args) (funcall func result (car rest))))
((null rest) result)))
(defun same-length-p (l1 l2)
(if (eq l1 '())
(eq l2 '())
(if (eq l2 '())
nil
(same-length-p (cdr l1) (cdr l2)))))
;;; CL macro defining stuff
(defvar *macro-expanders* (make-hash-table :test #'eq))
(defvar *compiler-macro-expanders* (make-hash-table :test #'eq))
(defvar *type-macro-expanders* (make-hash-table))
(defvar *macroexpand-hook-w* #'funcall
"Function used to invoke macro expansion functions")
(defstruct macro-env
macros
symbol-macros)
(defstruct basic-macro
original-arg-list
expansion-function)
(defstruct (macro (:include basic-macro)))
(defstruct (compiler-macro (:include basic-macro)))
(defstruct (type-macro (:include basic-macro)))
(defmacro defmacro-w (name lambda-list &body body)
`(define-macro ',name
,(parse-macro-definition name lambda-list nil body)))
(defmacro deftype-w (name lambda-list &body body)
`(define-type
',name
,(parse-macro-definition name lambda-list '* body)))
(defmacro define-compiler-macro-w (name lambda-list &body body)
`(define-compiler-macro-1 ',name
,(parse-macro-definition name lambda-list nil body)))
(load "../cl/functions/cross-macros.lisp")
;;; ADD - make &body (body decls) destructure with PARSE-BODY
(defun parse-macro-definition (name args optional-default body)
(let ((args-without-&body (subst '&rest '&body args)))
(multiple-value-bind (whole-arg args-without-whole)
(if (eq (car args-without-&body) '&whole)
(values (second args-without-&body) (cddr args-without-&body))
(values (gensym "WHOLE") args-without-&body))
(multiple-value-bind (env-arg args-without-macro-stuff)
(let ((env (member '&environment args-without-whole :test #'eq)))
(if (null env)
(values (gensym "ENV") args-without-whole)
(values (second env)
(append (upto '&environment args-without-whole)
(cddr env)))))
(let ((dbind-list (if (null optional-default)
args-without-macro-stuff
(insert-optional-default
args-without-macro-stuff
`(quote ,optional-default)))))
`(function (lambda (,whole-arg ,env-arg)
(declare (ignoreable ,env-arg))
(block ,name
(destructuring-bind ,@(if (null dbind-list)
'(nil nil)
`(,dbind-list (cdr ,whole-arg)))
(block ,name
,@body))))))))))
;;; TODO: Make it do nice error checking and reporting? Use
;;; it to replace the pattern matcher in some cases?
;;; DO NOT USE THIS???
;;; The expansion could be made more efficient (fewer cars/cdrs)
;;; if we factor out common subexpressions.
(defmacro destructure ((vars form) &body body)
(labels ((walk-vars (expr path)
(if (atom expr)
(if (null expr)
expr
`((,expr ,path)))
(append (walk-vars (car expr) `(car ,path))
(walk-vars (cdr expr) `(cdr ,path))))))
(let ((f (gensym "FORM-")))
`(let ((,f ,form))
(let ,(walk-vars vars f) ,@body)))))
;;; HEY! I think key's should get the same treatment, but the
;;; manual doesn't think to say so....
(defun insert-optional-default (lambda-list default)
(loop for x in lambda-list
for optional? = (or (and optional?
(not (member x lambda-list-keywords
:test #'eq)))
(eq x '&optional))
collect (if (and (not (eq x '&optional)) optional?)
(typecase x
(symbol `(,x ,default))
(list `(,(first x) ,default ,@(cddr x))))
x)))
(defun macro-function-w (symbol)
(let ((expander (lookup-macro-expander symbol *macro-expanders* nil)))
(if (null expander)
nil
(basic-macro-expansion-function expander))))
(defun compiler-macro-function-w (name &optional env)
(declare (ignore env))
(gethash name *compiler-macro-expanders*))
(defun macro-arg-list (symbol table)
(let ((expander (lookup-macro-expander symbol table nil)))
(if (null expander)
nil
(basic-macro-original-arg-list expander))))
(defun define-macro-function (symbol function arg-list table constructor)
(setf (gethash symbol table)
(funcall constructor
:expansion-function function
:original-arg-list arg-list))
symbol)
(defun macroexpand-w (form &optional local-macro-env)
(expand-macro form *macro-expanders* local-macro-env t nil))
(defun macroexpand-1-w (form &optional local-macro-env)
(expand-macro form *macro-expanders* local-macro-env nil nil))
(defun compiler-macroexpand-w (form &optional local-macro-env)
(expand-macro form *compiler-macro-expanders* local-macro-env t nil))
(defun compiler-macroexpand-1-w (form &optional local-macro-env)
(expand-macro form *compiler-macro-expanders* local-macro-env nil nil))
(defun expand-macro (form table menv
repeat? original-call-is-a-macro?)
(if (atom form)
(let ((def (lookup-symbol-macro-def form menv)))
(if (null def)
(values form original-call-is-a-macro?)
(values (second def) t)))
(if (atom (car form))
(let ((expander (lookup-macro-expander (car form)
table
menv)))
(if (null expander)
(values form original-call-is-a-macro?)
(let ((exp (funcall *macroexpand-hook-w*
(basic-macro-expansion-function expander)
form
menv)))
(if (and repeat? (not (eq form exp)))
(expand-macro exp table menv repeat? t)
(values exp t)))))
(values form original-call-is-a-macro?))))
(defun lookup-macro-expander (name table menv)
(let ((local (and (not (null menv))
(assoc name (macro-env-macros menv) :test #'eq))))
(if (null local)
(gethash name table)
(cdr local))))
(defun lookup-symbol-macro-def (name menv)
(and (not (null menv))
(assoc name (macro-env-symbol-macros menv) :test #'eq)))
(defun remove-macro-expander (name)
(remhash name *macro-expanders*))
(defun remove-compiler-macro-expander (name)
(remhash name *compiler-macro-expanders*))
(defun remove-type-macro-expander (name)
(remhash name *type-macro-expanders*))
(defun parse-in/out (spec)
(multiple-value-bind (i o)
(if (member '=> spec :test #'eq)
(values (subseq spec 0 (position '=> spec))
(subseq spec (1+ (position '=> spec))))
(values spec nil))
(values (mapcar #'first i)
(mapcar #'first o)
(mapcar #'second i)
(mapcar #'second o))))
(defun quoted-constant-p (l)
(and (listp l)
(eq (first l) 'quote)
(null (cddr l))))
(deftype lambda-expr ()
'(satisfies lambda-expr?))
;;; Condition system thing.
(defmacro with-keyword-pairs ((names expression &optional keywords-var)
&body forms)
(let ((temp (member '&rest names)))
(unless (= (length temp) 2)
(error "&REST keyword is ~:[missing~;misplaced~]." temp))
(let ((key-vars (ldiff names temp))
(key-var (or keywords-var (gensym)))
(rest-var (cadr temp)))
(let ((keywords (mapcar #'(lambda (x)
(intern (string x)
*keyword-package*))
key-vars)))
`(multiple-value-bind (,key-var ,rest-var)
(parse-keyword-pairs ,expression ',keywords)
(let ,(mapcar #'(lambda (var keyword)
`(,var (getf ,key-var ,keyword)))
key-vars keywords)
,@forms))))))